package com.zdc.encrypt;

import cn.hutool.core.io.FileUtil;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.StringUtils;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;

import java.nio.file.Files;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author zdc
 * @description
 * @date 2025/8/14 11:42
 */
public class EncryptedConfigInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext context) {
        try {
            // 1. 获取加密密码
            String password = context.getEnvironment().getProperty("PASSWORD");
            if (password == null) {
                throw new IllegalStateException("加密密码(PASSWORD)未配置");
            }
            MultiYamlEncryptor encryptor = new MultiYamlEncryptor(password, null);
            // 2. 先加载主配置文件
            try {
                loadConfig(context, encryptor, "application.yml.gz", "mainConfig");
            } catch (Exception e1) {
                try {
                    loadConfig(context, encryptor, "bootstrap.yml.gz", "mainConfig");
                } catch (Exception e2) {
                    /*e2*/
                }
            }
            // 3. 按特定顺序加载其他配置文件
            String activeProfile = context.getEnvironment().getActiveProfiles().length > 0 ? context.getEnvironment().getActiveProfiles()[0] : "default";
            try {
                loadConfig(context, encryptor, "application-" + activeProfile + ".yml.gz", activeProfile + "Config");
            } catch (Exception e3) {
                try {
                    loadConfig(context, encryptor, "bootstrap-" + activeProfile + ".yml.gz", activeProfile + "Config");
                } catch (Exception e4) {
                    /*e4*/
                }
            }
            String include = context.getEnvironment().getProperty("spring.profiles.include");
            if (StringUtils.hasText(include)){
                String[] includes = include.split(",");
                Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath*:*.yml.gz");
                for (Resource resource : resources) {
                    if (!"application.yml.gz".equals(resource.getFilename()) && !"bootstrap.yml.gz".equals(resource.getFilename())) {
                        for (String inc : includes) {
                            if (Objects.requireNonNull(resource.getFilename()).contains(("-" + inc + "."))) {
                                loadConfig(context, encryptor, resource.getFilename(), Objects.requireNonNull(resource.getFilename()).replace(".yml.gz", ""));
                            }
                        }
                    }
                }
//            loadConfig(context, encryptor, "application-http.yml.gz", "encryptedConfigHttp");
//            loadConfig(context, encryptor, "application-pageoffice.yml.gz", "encryptedConfigPageOffice");
            }
        } catch (Exception e) {
            throw new RuntimeException("初始化加密配置失败", e);
        }
        try {
            /*addby zhaodecang@2025/8/15 15:54:参数适配调整*/
            ConfigurableEnvironment environment = context.getEnvironment();
            String posyspath = environment.getProperty("posyspath");
            String cnfName = "application-pageoffice";
            if (posyspath != null && !FileUtil.exist(posyspath)) {
                LinkedHashMap<String, Object> config = new LinkedHashMap<>();
                MapPropertySource mapPropertySource = new MapPropertySource(cnfName, config);
                config.put("posyspath", Objects.requireNonNull(this.getClass().getClassLoader().getResource("pageoffice/lic")).getPath().substring(1));
                config.put("docpath", Objects.requireNonNull(this.getClass().getClassLoader().getResource("pageoffice/doc")).getPath().substring(1));
                context.getEnvironment().getPropertySources().replace(cnfName, mapPropertySource);
            }
        } catch (Exception e) {
            /*throw new RuntimeException(e);*/
        }
    }

    private void loadConfig(ConfigurableApplicationContext context, MultiYamlEncryptor encryptor, String configFile, String propertySourceName) throws Exception {
        ClassPathResource resource = new ClassPathResource(configFile);
        if (!resource.exists()) {
            return;
        }
        byte[] encrypted = Files.readAllBytes(resource.getFile().toPath());
        String decrypted = encryptor.decryptYaml(encrypted);
        Yaml yaml = new Yaml(new Constructor());
        Map<String, Object> combinedConfig = new LinkedHashMap<>();
        // 合并所有文档的配置
        for (Object document : yaml.loadAll(decrypted)) {
            if (document instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> configMap = (Map<String, Object>) document;
                combinedConfig.putAll(flattenMap(configMap));
            }
        }
        // 确保Redis配置在顶层
        if (combinedConfig.containsKey("spring.redis")) {
            @SuppressWarnings("unchecked")
            Map<String, Object> redisConfig = (Map<String, Object>) combinedConfig.get("spring.redis");
            redisConfig.forEach((key, value) -> {
                combinedConfig.put("spring.redis." + key, value);
            });
        }
        // 添加到环境最前面
        context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(propertySourceName, combinedConfig));
    }

    private Map<String, Object> flattenMap(Map<String, Object> source) {
        Map<String, Object> result = new LinkedHashMap<>();
        flattenMap("", source, result);
        return result;
    }

    private void flattenMap(String prefix, Map<String, Object> source, Map<String, Object> result) {
        source.forEach((key, value) -> {
            String fullKey = prefix.isEmpty() ? key : prefix + "." + key;
            if (value instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> nestedMap = (Map<String, Object>) value;
                flattenMap(fullKey, nestedMap, result);
            } else {
                result.put(fullKey, value);
            }
        });
    }
}